package model;

import java.util.Vector;

import shared.ConcreteLock;
import shared.Lock;

/**
 * The ComponentCommon class contains the name, price and state of a Component.
 * @author hfi411pi
 */
public abstract class ComponentCommon extends Observee {

	private final String name;
	private int price;
	private final Observer observer;
	private ComponentState state;
	
	public Observer getObserver() {
		return observer;
	}

	public ComponentState getState() {
		return state;
	}

	public void setState(final ComponentState state) {
		this.state = state;
	}

	/**
	 * Provides the name of this Component
	 * @return the name of this Component.
	 */
	public String getName() {
		return name;
	}
	

	/**
	 * Provides the price of this Component.
	 * @return the price of this Component.
	 */
	public int getPrice() {
		return price;
	}
	
	/**
	 * Sets the price of this Component.
	 * @param newPrice is the new price of this Component.
	 */
	public void setPrice(final int newPrice){
		if(this.price != newPrice){
			this.price = newPrice;
			this.getState().refresh(PriceChangedEvent.create());
		}
	}

	/**
	 * Provides the overall price of this Component.
	 * @return the overall price of this Component.
	 */
	public int getOverallPrice(){
		this.overallPriceLock.lock();
		final int result = this.getState().getOverallPrice();
		this.overallPriceLock.unlock();
		return result;
	}

	Lock overallPriceLock = new ConcreteLock(false);
	
	/**
	 * Calculates the new overall price.
	 * @return the new overall price.
	 */
	public int calculateNewOverallPrice(){
		return this.getPrice() + this.calculateNewOverallPriceTransitive();
	}
	/**
	 *  Calculates the new transitive overall price.
	 * @return the new transitive overall price.
	 */
	public abstract int calculateNewOverallPriceTransitive();
	
	/**
	 * Constructor of ComponentCommon.
	 * Sets the State of this Component to NotCached.
	 * Initializes the Observer by passing the event on update(Event event) to the refresh(Event event) method on the state.
	 * @param name is the name of this Component.
	 * @param price is the price of this Component.
	 */
	protected ComponentCommon(final String name, final int price) {
		this.setState(NotCached.create(this));
		this.name = name;
		this.price = price;
		this.observer = new Observer() {
			
			@Override
			public void update(final Event event) {
				ComponentCommon.this.getState().refresh(event);
			}
		};
	}

	@Override
	public String toString(){
		return this.getName() + PartsListConstants.leftSquareBracket +
				this.getPrice() + PartsListConstants.rightSquareBracket + this.getState().toString();
	}

	/**
	 * Returns true if this component contains the provided Component.
	 * @param component is the Component to search for.
	 * @return	true if this Component is the provided Component.
	 * 			true if this component contains the provided Component.
	 * 			false if this component does not contain the provided Component
	 */
	public boolean contains(final ComponentCommon component){
		return this.equals(component)||this.containsTransitive(component);
	}
	/**
	 * Provides true if any parts of this CommonComponent contain the provided component.
	 * @param component is the Component to search for.
	 * @return	true if any parts contain the provided Component.
	 * 			false if none of the parts contain the provided Component.
	 */
	public abstract boolean containsTransitive(ComponentCommon component);

	/**
	 * Calculates the new MaterialList.
	 * @return the newly calculated MaterialList.
	 */
	public abstract MaterialList calculateNewMaterialList();
	
	
	/** Adds a part to this component.
	 * @param part is the part to add.
	 * @param amount is the quantity of the part to add.
	 * @throws Exception if this component is a part of the part that should be added.
	 * @throws Exception if you are trying to add a part to a material.
	 */
	public abstract void addPart(ComponentCommon part, int amount) throws Exception;
	
	
	/**
	 * Provides all direct parts of this Component.
	 * @return a vector of all direct parts.
	 */
	public abstract  Vector<QuantifiedComponent> getDirectParts();
	
	/**
	 * Provides the number of Materials this Component requires.
	 * @return the number of Materials.
	 */
	public abstract  int getNumberOfMaterials();
	
	
	
	/**
	 * Provides a list with all Materials and their quantity of this Component.
	 * @return the MaterialList of this Component.
	 */
	public abstract  MaterialList getMaterialList();
}